﻿using System;
using System.Collections.Generic;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using Evolution.Utilities;
using Evolution.Security;

namespace Evolution.UI.WebParts
{               
    public class PageActionEditor : EditorPart
    {
        #region Fields and Consts

        private ActionList _internalSupportedActions = new ActionList();
        private AssertionList _internalSupportedAssertion = new AssertionList();

        private Dictionary<string, object> _clientVariables = new Dictionary<string, object>();
 
        #endregion

        #region Constructors
       
        public PageActionEditor(string id)
        {
            this.ID = "PageActionEditor_" + id;
            this.Title = "Page Action Settings";

            this.InternalSupportedActions.Add(new AccessIsDeniedAction());
            this.InternalSupportedActions.Add(new NotFoundAction());
            this.InternalSupportedActions.Add(new RedirectToAction());
            this.InternalSupportedActions.Add(new TransferToErrorPageAction());
            this.InternalSupportedActions.Add(new ShowContentsAction());
            this.InternalSupportedActions.Add(new HidePageElementsAction());
            this.InternalSupportedActions.Add(new TrackAccessToPageAction());
            this.InternalSupportedActions.Add(new ManageWebPartsAction());
            
            this.InternalSupportedAssertion.Add(new CurrentUserAssertion());
            this.InternalSupportedAssertion.Add(new QueryStringAssertion());
            this.InternalSupportedAssertion.Add(new FormAssertion());
            this.InternalSupportedAssertion.Add(new ParamsAssertion());
            this.InternalSupportedAssertion.Add(new CookieAssertion());
            this.InternalSupportedAssertion.Add(new ServerVariableAssertion());
            this.InternalSupportedAssertion.Add(new PageActionAssertion());
            this.InternalSupportedAssertion.Add(new PageAssertion());
            this.InternalSupportedAssertion.Add(new ExpressionAssertion());
            this.InternalSupportedAssertion.Add(new PagePropertyAssertion() );
            this.InternalSupportedAssertion.Add(new WebPropertyAssertion());
            this.InternalSupportedAssertion.Add(new SessionAssertion());
            this.InternalSupportedAssertion.Add(new ApplicationAssertion());
        } 

        #endregion

        #region Properties

        protected ActionList InternalSupportedActions
        {
            get { return this._internalSupportedActions; }
        }

        protected AssertionList InternalSupportedAssertion
        {
            get { return this._internalSupportedAssertion; }
        }

        public PageActionWebPart PageActionWebPart
        {
            get { return this.WebPartToEdit as PageActionWebPart; }
        }

        public int AssertionCount
        {
            get
            {
                if (this.ViewState["AssertionCount"] == null)
                    return this.PageActionWebPart.Condition.Count;

                return Convert.ToInt32(this.ViewState["AssertionCount"]);
            }
            set { this.ViewState["AssertionCount"] = value; }
        }

        #endregion

        #region Methods

        public void AddClientVariable(string name, string value)
        {
            if(StringUtility.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");

            this._clientVariables[name] = value;
        }

        public void AddClientArray(string name, string value)
        {
            List<string> list = null; 

            if (StringUtility.IsNullOrEmpty(value))
                throw new ArgumentNullException("value");

            if (this._clientVariables.ContainsKey(name))
            {
                string text = this._clientVariables[name] as string;                     
                if (text != null)
                    throw new InvalidCastException(String.Format("The variable '{0}' is already in use and is not an array.", name));

                list = this._clientVariables[name] as List<string>;
            }
            else
                this._clientVariables[name] = list = new List<string>();

            list.Add(value);
        }

        protected override void OnPreRender(EventArgs e)
        {
            this.Parent.Controls[2].Visible = false;
            this.Parent.Controls[3].Visible = false;

            if (!this.Page.ClientScript.IsStartupScriptRegistered(this.GetType(), "PageActionJSStartupScript"))
            {
                #region Client variables and arrays
                                
                string clientVars = String.Empty;

                Dictionary<string,object>.Enumerator enumerator = this._clientVariables.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    List<string> list = enumerator.Current.Value as List<string>;
                    if (list != null)
                    {
                        string values = String.Empty;
                        foreach (string s in list)
                            values += s + ", ";

                        if (!String.IsNullOrEmpty(values))
                            values = values.Remove(values.Length - 2);

                        clientVars += String.Format("var {0} = [ {1} ];\n", enumerator.Current.Key, values);
                    }
                    else
                        clientVars += String.Format("var {0} = {1};\n", enumerator.Current.Key, enumerator.Current.Value);
                }

                #endregion
               
                this.Page.ClientScript.RegisterStartupScript(
                    this.GetType(),
                    "PageActionJSStartupScript", String.Format(@"{0}", clientVars),
                    true);

                this.Page.ClientScript.RegisterStartupScript(this.GetType(),
                    "PageActionJSInclude",
                    "<script src=\"/_layouts/Evolution/js/PageActionEditor.js\" type=\"text/javascript\"></script>", 
                    false);
            }

            base.OnPreRender(e);
        }

        protected override void CreateChildControls()
        {
            SPWeb web = SPContext.Current.Web;
            PageActionWebPart parent = this.WebPartToEdit as PageActionWebPart;

            ControlCollectionBuilder builder = new ControlCollectionBuilder(this);
            
            if (!parent.Owners.IsEmpty && !parent.Owners.CheckCurrentUser(SPContext.Current.Web))
            {
                ToolPane pane = (ToolPane)this.Parent;
                ((Button)pane.Apply).Enabled = false;
                ((Button)pane.OK).Enabled = false;

                builder.AddStaticText(@"
                <table cellspacing=""0"" border=""0"" style=""border-width:0px;width:100%;border-collapse:collapse;"">
                    <tr>
                        <td>
                            <div class=""UserGeneric"">You may not have permission to configure this Page Action Web Part.<br/><br/>Contact the owners of this Web Part to find out if you have access permission.</div>
                        </td>
                    </tr>
                </table>");

                builder.Flush();
                
                return;
            }

            string text1 = @"
                <table cellspacing=""0"" border=""0"" style=""border-width:0px;width:100%;border-collapse:collapse;"">
                    <tr>
                        <td>
                            <div class=""UserGeneric"">";

            builder.AddStaticText(text1);

            #region Actions

            text1 = @"<div class=""UserSectionHead ms-bold"">Actions</div>
                      <div class=""UserControlBody"" style=""margin-bottom: 5px"">Specify which actions to take.</div>";

            builder.AddStaticText(text1);

            //int j = 0;
            DropDownList list = new DropDownList();
            list.ID = "Evolution_Pawp_ListActions";
            list.Attributes["onchange"] = "Evolution_Pawp_ChangeAction(this)";
            list.Style[HtmlTextWriterStyle.Width] = "175px";
            list.ToolTip = "Select an action";

            this.InternalSupportedActions.AddRange(parent.SupportedActions);
          
            bool bool1 = false;
            foreach (BaseAction action in this.InternalSupportedActions)
            {
                if(action.GetType().Equals(parent.Action.GetType()))
                    bool1 = true;

                ListItem item = new ListItem(action.Caption);
                list.Items.Add(item);
            }
            if (!bool1 && !parent.Action.GetType().Equals(typeof(EmptyAction)))
            {
                ListItem item = new ListItem(parent.Action.Caption);
                list.Items.Add(item);

                this.InternalSupportedActions.Add(parent.Action);
            }

            builder.AddControl(list);

            builder.AddErrorPlaceholder("ListActions");

            this.AddClientVariable("Evolution_Pawp_ClientID", "\"" + list.ClientID + "\"");

            #region Create action panels

            int actionsIndex = 0;
            string actionsIdArray = String.Empty;
            foreach (BaseAction action in this.InternalSupportedActions)
            {
                string containerId = String.Format("Evolution_Pawp_ActionContainer{0}", actionsIndex++);
                builder.AddStaticText(String.Format("<div id=\"{0}\">", containerId));
                action.CreateInterface(builder, this);
                builder.AddStaticText("</div>");

                actionsIdArray += "'" + containerId + "', ";
            }            
            if(!String.IsNullOrEmpty(actionsIdArray))
                actionsIdArray = actionsIdArray.Substring(0, actionsIdArray.Length - 2);

            this.Page.ClientScript.RegisterArrayDeclaration("Evolution_Pawp_ActionContainersID", actionsIdArray);

            #endregion

            #endregion

            #region Condition

            builder.AddStaticText("<br/>");

            builder.AddSeperator();

            builder.AddStaticText(@"<div class=""UserSectionHead ms-bold"">Condition</div>
                                    <div class=""UserControlBody"" style=""margin-bottom: 5px;"">Create the condition for determining when to execute the actions indicated.</div>");

            builder.Flush();

            this.InternalSupportedAssertion.AddRange(parent.SupportedAssertions);

            PlaceHolder assertionPanel = new PlaceHolder();
            assertionPanel.ID = "AssertionPanel";

            builder.AddControl(assertionPanel);

            ControlCollectionBuilder builder2 = new ControlCollectionBuilder(assertionPanel);

            for (int i = 0; i < this.AssertionCount; i++)
                this.AddClientArray("Evolution_Pawp_AssertionClientIDs", "'" + this.CreateAssertion(i, builder2) + "'");

            #region "Add new assertion..." button

            builder.AddStaticText("<br/>");

            LinkButton addNewAssertion = new LinkButton();
            addNewAssertion.ID = "NewAssertion";
            addNewAssertion.Text = "Add new assertion...";
            addNewAssertion.Click += new EventHandler(this.AddNewAssertion_Click);
            addNewAssertion.ToolTip = "Adds a new assertion";

            builder.AddControl(addNewAssertion);

            builder.AddStaticText("<br/>");

            #endregion

            #region "Remove assertion..." button

            LinkButton removeAssertion = new LinkButton();
            removeAssertion.ID = "RemoveAssertion";
            removeAssertion.Text = "Remove assertion...";
            removeAssertion.Style[HtmlTextWriterStyle.Display] = "block";
            removeAssertion.Click += new EventHandler(this.RemoveAssertion_Click);
            removeAssertion.ToolTip = "Remove the last assertion added";
            removeAssertion.Visible = this.AssertionCount != 0;

            builder.AddControl(removeAssertion);

            #endregion
                        
            #endregion

            builder.AddErrorPlaceholder("Condition");

            #region Owners

            builder.AddStaticText("<br/>");

            builder.AddSeperator();

            builder.AddStaticText(@"<div class=""UserSectionHead ms-bold"">Owners</div>
                                    <div class=""UserControlBody"" style=""margin-bottom: 5px;"">Select the users who can change the configuration of the Web Part.</div>");

            PeopleEditor owners = new PeopleEditor();
            owners.AllowEmpty = true;
            owners.AllowTypeIn = true;
            owners.MultiSelect = true;
            owners.PlaceButtonsUnderEntityEditor = false;
            owners.SelectionSet = "User,DL,SecGroup,SPGroup";
            owners.Width = Unit.Pixel(200);
            owners.Rows = 2;
            owners.ValidatorEnabled = true;
            owners.ID = "Evolution_Pawp_Owners";

            builder.AddControl(owners);

            #endregion

            #region Appearance (override standard)

            builder.AddStaticText("<br/>");

            builder.AddSeperator();

            builder.AddStaticText(@"<div class=""UserSectionHead ms-bold"">Appearance</div>
                                    <div class=""UserSectionHead"" title=""Text heading at the top of the Web Part."" style=""margin-top: 5px"">Title</div>");

            TextBox title = new TextBox();
            title.ID = "Evolution_Pawp_WebPartTitle";
            title.CssClass = "UserInput";
            title.Style[HtmlTextWriterStyle.Width] = "175px";

            builder.AddTextBoxAndBuilder(title, false);

            builder.AddStaticText(@"<div class=""UserSectionHead"" title=""Users to wich show the errors generated by the Web Part."" style=""margin-top: 5px"">Error notification mode</div>");

            DropDownList errorNotificationMode = new DropDownList();
            errorNotificationMode.ID = "Evolution_Pawp_ErrorNotificationMode";
            errorNotificationMode.Width = Unit.Pixel(175);
            errorNotificationMode.ToolTip = "Error notification mode";
            errorNotificationMode.Items.Add(new ListItem("Never", ErrorNotificationMode.Never.ToString()));
            errorNotificationMode.Items.Add(new ListItem("Always", ErrorNotificationMode.Always.ToString()));
            errorNotificationMode.Items.Add(new ListItem("Only to site administrators", ErrorNotificationMode.OnlyToSiteAdmins.ToString()));
            errorNotificationMode.Items.Add(new ListItem("Only to owners", ErrorNotificationMode.OnlyToOwners.ToString()));
            errorNotificationMode.Items.Add(new ListItem("Both site admins and owners", ErrorNotificationMode.BothSiteAdminsAndOwners.ToString()));

            builder.AddControl(errorNotificationMode);
            
            #endregion

            builder.AddStaticText("</div></td></tr></table>");

            builder.Flush();
        }

        public override bool ApplyChanges()
        {
            PageActionWebPart parent = this.WebPartToEdit as PageActionWebPart;
            if (parent != null)
            {
                this.EnsureChildControls();

                #region Action

                int listActionIndex = (this.FindControl("Evolution_Pawp_ListActions") as DropDownList).SelectedIndex;
                BaseAction selectedAction = this.InternalSupportedActions[listActionIndex];

                if (selectedAction.RequireCondition && this.FindControl("AssertionSubject0") == null)
                    this.RaiseError("Condition", "Add a condition.", String.Format("The action '{0}' requires the addition of a condition.", selectedAction.Caption));

                selectedAction.ApplyChanges(this);

                parent.Action = selectedAction;

                #endregion

                #region Condition

                Condition temp = new Condition();

                for (int j = 0; j < this.AssertionCount; j++)
                {
                    BaseAssertion assertion1 = this.InternalSupportedAssertion[(this.FindControl("AssertionSubject" + j) as DropDownList).SelectedIndex];
                    BaseAssertion assertion2 = (BaseAssertion)Activator.CreateInstance(assertion1.GetType());

                    assertion2.ApplyChanges(this, j);
                    if (j > 0)
                        temp.Add((BooleanOperators)Enum.Parse(typeof(BooleanOperators), (this.FindControl("AssertionOperatorsList" + j) as DropDownList).SelectedValue), assertion2);
                    else
                        temp.Add(assertion2);
                }
                parent.Condition = temp;                

                #endregion

                #region Owners

                PeopleEditor owners = this.FindControl("Evolution_Pawp_Owners") as PeopleEditor;

                SecurityEntity se1 = new SecurityEntity();
                bool check1 = false;
                foreach (PickerEntity entity in owners.Entities)
                {
                    if (!entity.IsResolved)
                        continue;

                    string account = entity.Description;
                    string type = Convert.ToString(entity.EntityData["PrincipalType"]);
                    se1.Add((PrincipalType)Enum.Parse(typeof(PrincipalType), type, true), account);
                    check1 = true;
                }
                parent.Owners = check1 ? se1 : SecurityEntity.Empty;
                
                #endregion

                #region Appearance (override standard)

                TextBox title = this.FindControl("Evolution_Pawp_WebPartTitle") as TextBox;
                parent.Title = title.Text;

                DropDownList errorNotifyMode = this.FindControl("Evolution_Pawp_ErrorNotificationMode") as DropDownList;
                parent.ErrorNotificationMode = (ErrorNotificationMode)Enum.Parse(typeof(ErrorNotificationMode), errorNotifyMode.SelectedValue);

                #endregion
            }
            else
                return false;

            return true;
        }

        public override void SyncChanges()
        {
            this.EnsureChildControls();

            PageActionWebPart parent = this.WebPartToEdit as PageActionWebPart;
            if (parent != null)
            {
                if (!parent.Owners.IsEmpty && !parent.Owners.CheckCurrentUser(SPContext.Current.Web))
                    return;
                
                #region Action

                int counter = 0;
                Type actionType = parent.Action.GetType();
                foreach (BaseAction action in this.InternalSupportedActions)
                {
                    if (action.GetType().Equals(actionType))
                    {
                        ((DropDownList)this.FindControl("Evolution_Pawp_ListActions")).SelectedIndex = counter;
                        break;
                    }
                    counter++;
                }
                parent.Action.SyncChanges(this);

                #endregion

                #region Condition

                for (int j = 0; j < parent.Condition.AssertionElements.Length; j++)
                {
                    AssertionElement element = parent.Condition.AssertionElements[j];

                    if(j != 0)
                        ((DropDownList)this.FindControl("AssertionOperatorsList" + j)).SelectedValue = element.Operator.ToString();

                    counter = 0;
                    DropDownList subject = this.FindControl("AssertionSubject" + j) as DropDownList;
                    Type assertionType = element.Assertion.GetType();
                    foreach (BaseAssertion assertion in this.InternalSupportedAssertion)
                    {
                        if (assertion.GetType().Equals(assertionType))
                        {
                            subject.SelectedIndex = counter;
                            break;
                        }
                        counter++;
                    }
                    element.Assertion.SyncChanges(this, j);
                }
                
                #endregion

                #region Owners

                if (!parent.Owners.IsEmpty)
                {
                    PeopleEditor owners = this.FindControl("Evolution_Pawp_Owners") as PeopleEditor;
                    owners.CommaSeparatedAccounts = parent.Owners.ToString(",");
                }

                #endregion

                #region Appearance (override standard)

                TextBox title = this.FindControl("Evolution_Pawp_WebPartTitle") as TextBox;
                title.Text = parent.Title;

                DropDownList errorNotifyMode = this.FindControl("Evolution_Pawp_ErrorNotificationMode") as DropDownList;
                errorNotifyMode.SelectedValue = parent.ErrorNotificationMode.ToString();

                #endregion
            }
        }        

        #region Event handlers

        private void AddNewAssertion_Click(object sender, EventArgs e)
        {
            LinkButton addNewAssertion = sender as LinkButton;

            this.AssertionCount = this.AssertionCount + 1;

            PlaceHolder assertionPanel = addNewAssertion.Parent.FindControl("AssertionPanel") as PlaceHolder;

            ControlCollectionBuilder builder2 = new ControlCollectionBuilder(assertionPanel);

            this.AddClientArray("Evolution_Pawp_AssertionClientIDs", "'" + this.CreateAssertion(this.AssertionCount - 1, builder2) + "'");

            if (this.AssertionCount == 1)
                addNewAssertion.Parent.FindControl("RemoveAssertion").Visible = true;
        }

        private void RemoveAssertion_Click(object sender, EventArgs e)
        {
            LinkButton removeAssertion = sender as LinkButton;

            Control assertionPanel = removeAssertion.Parent.FindControl("AssertionPanel");

            this.AssertionCount = this.AssertionCount - 1;
            if (this.AssertionCount == 0)
            {
                assertionPanel.Controls.Clear();
                removeAssertion.Visible = false;
            }
            else
            {
                int index = assertionPanel.Controls.IndexOf(assertionPanel.FindControl("AssertionMarker" + this.AssertionCount));
                for (int i = assertionPanel.Controls.Count - 1; i >= index; i--)
                    assertionPanel.Controls.RemoveAt(i);
            }            
        }     

        #endregion

        #region Helpers Methods

        public string CreateAssertion(int index, ControlCollectionBuilder builder)
        {
            LiteralControl marker = new LiteralControl();
            marker.ID = "AssertionMarker" + index;
            builder.AddControl(marker);

            #region Boolean operator (And, Or)

            if (index == 0)
                builder.AddStaticText("<div style=\"margin-bottom: 5px;\">Perform action when</div>");
            else
            {
                builder.AddStaticText("<br/>");

                DropDownList operators = new DropDownList();
                operators.ID = "AssertionOperatorsList" + index;
                operators.Items.Add(new ListItem("And", BooleanOperators.And.ToString()));
                operators.Items.Add(new ListItem("Or", BooleanOperators.Or.ToString()));
                operators.Style[HtmlTextWriterStyle.MarginBottom] = "5px";

                builder.AddControl(operators);

                builder.AddStaticText("<br/><div style=\"margin-bottom: 5px;\">when</div>");
            }

            #endregion

            #region Assertion subject

            DropDownList subject = new DropDownList();
            subject.ID = "AssertionSubject" + index;
            subject.Width = Unit.Pixel(150);
            subject.ToolTip = "Select a subject for the assertion";
            subject.Attributes["onchange"] = "Evolution_Pawp_ChangeAssertionSubject(this.selectedIndex, " + index + ")";
            foreach (BaseAssertion assertion in this.InternalSupportedAssertion)
                subject.Items.Add(new ListItem(assertion.Caption));

            builder.AddControl(subject);

            builder.AddStaticText("<br/><br/>");

            #endregion

            #region Assertion body

            int j = 0;
            foreach (BaseAssertion assertion in this.InternalSupportedAssertion)
            {
                builder.AddStaticText("<div ID=\"AssertionContainer{0}_{1}\">", index, j++);
                assertion.CreateInterface(builder, this, index);
                builder.AddStaticText("</div>");
            }

            #endregion

            builder.Flush();

            return subject.ClientID;
        }

        public void RaiseError(string id, string message, string summary)
        {
            LiteralControl error = this.FindControl("Evolution_Pawp_" + id + "_Err") as LiteralControl;
            error.Text = "<div style=\"color: rgb(224, 0, 0);\">" + message + "</div>";
            error.Visible = true;

            throw new WebPartPageUserException(summary);
        }

        public void RaiseError(string id, string message)
        {
            this.RaiseError(id, message, String.Empty);
        }

        #endregion

        #endregion
    }
}







